﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.IO;

namespace ElevatorLib
{
    /// <summary>
    /// 客流生成类
    /// The type of passenger flow generation
    /// </summary>
    public class PersonCreateBaseOD : AbstractPersonCreator
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public PersonCreateBaseOD()
        {
            name = "一种基于OD矩阵的客流仿真数据";
            author = "ability";
            description = "乘客到达时间为泊松分布，起始楼层和目标楼层通过OD矩阵计算，其他参数为默认值";
            conf = new ODConfig("./PassengerCreator/personCreate.conf");
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configureFile">配置文件</param>
        public PersonCreateBaseOD(string configureFile)
        {
            name = "一种基于OD矩阵的客流仿真数据";
            author = "ability";
            description = "乘客到达时间为泊松分布，起始楼层和目标楼层通过OD矩阵计算，其他参数为默认值";
            conf = new ODConfig(configureFile);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configureFile">配置文件</param>
        /// <param name="personDataFile">客流文件</param>
        public PersonCreateBaseOD(string configureFile, string personDataFile):base(personDataFile)
        {
            name = "一种基于OD矩阵的客流仿真数据";
            author = "ability";
            description = "乘客到达时间为泊松分布，起始楼层和目标楼层通过OD矩阵计算，其他参数为默认值";
            conf = new ODConfig(configureFile);
        }

        #region Properties
        /// <summary>
        /// 楼层高度
        /// The floor's height
        /// </summary>
        private int n;
        /// <summary>
        /// 乘客到达率λ
        /// passenger arrival rate λ
        /// </summary>
        private double Lambda;
        /// <summary>
        /// 各层分布人数
        /// The people distribute in each floor
        /// </summary>
        private int[] pop;
        /// <summary>
        /// 上行客流(起始楼层为基层)
        /// Upward passenger flow(initial floor is basic level)
        /// </summary>
        private double x;
        /// <summary>
        /// 下行客流(目标楼层为基层)
        /// Downward passenger flow(target floor is basic level)
        /// </summary>
        private double y;
        /// <summary>
        /// 层间客流
        /// Passenger flow between initial and target floor
        /// </summary>
        private double z;
        /// <summary>
        /// 生成的数据数量。
        /// </summary>
        private int num;
        /// <summary>
        /// 起始密度向量
        /// Initial density vector
        /// </summary>
        private double[] origin;
        /// <summary>
        /// 起始累计概率
        /// </summary>
        private double[] originQ;
        /// <summary>
        /// 目标矩阵
        /// Target matrix 
        /// </summary>
        private double[,] OD;
        /// <summary>
        /// 目标累计概率
        /// Target cumulative probability 
        /// </summary>
        private double[,] ODQ;
        /// <summary>
        /// 配置文件
        /// </summary>
        private ODConfig conf;
        #endregion

        #region Functions
        /// <summary>
        /// 实现客流生成
        /// </summary>
        protected override void CreatePerson()
        {
            Init();
            double initTime = 0;
            for (int i = 0; i < num; i++)
            {
                initTime = Creator(initTime);
            }
        }
        
        /// <summary>
        /// 初始化
        /// </summary>
        private void Init()
        {
            #region 加载配置
            n = conf.ReadFloorNum();
            Lambda = conf.ReadLambda();
            pop = conf.ReadPOP();
            conf.ReadXYZ(out x, out y, out z);
            num = conf.ReadNum();
            #endregion

            #region 计算起始密度向量
            origin = new double[n];
            origin[0] = x;
            for (int i = 1; i < n; i++)
            {
                int sumOfPOP = 0;
                for (int j = 1; j < n; j++)
                {
                    sumOfPOP += pop[j];
                }
                origin[i] = (y + z) * pop[i] / Convert.ToDouble(sumOfPOP);
            }
            #endregion

            #region 起始密度和
            double F = 0;
            for (int i = 0; i < n; i++)
            {
                F += origin[i];
            }
            #endregion

            #region 各楼层累计概率
            originQ = new double[n];
            for (int i = 0; i < n; i++)
            {
                for (int k = 0; k <= i; k++)
                {
                    originQ[i] += origin[k] / F;
                }
            }
            #endregion

            #region 起始目标矩阵
            OD = new double[n, n];
            OD[0, 0] = 0;
            for (int j = 1; j < n; j++)
            {
                int sumOfPOP = 0;
                for (int k = 1; k < n; k++)
                {
                    sumOfPOP += pop[k];
                }
                OD[0, j] = (y + z) * pop[j] / Convert.ToDouble(sumOfPOP);
            }
            for (int i = 1; i < n; i++)
            {
                for (int j = 1; j < n; j++)
                {
                    if (i == j)
                    {
                        OD[i, j] = 0;
                    }
                    else
                    {
                        int sumOfPOP = 0;
                        for (int k = 1; k < n; k++)
                        {
                            if (k == j)
                            {
                                continue;
                            }
                            sumOfPOP += pop[k];
                        }
                        OD[i, j] = z * pop[j] / (Convert.ToDouble(sumOfPOP) * (y + z));
                    }
                }
            }
            for (int i = 1; i < n; i++)
            {
                OD[i, 0] = y / Convert.ToDouble(y + z);
            }
            #endregion

            #region 目标元素和
            double[] FI = new double[n];
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    if (i == j)
                    {
                        continue;
                    }
                    FI[i] += OD[i, j];
                }
            }
            double[,] ODP = new double[n, n];
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    ODP[i, j] = OD[i, j] / FI[i];
                }
            }
            #endregion

            #region 目标层累计概率
            ODQ = new double[n, n];
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    for (int k = 0; k <= j; k++)
                    {
                        ODQ[i, j] += ODP[i, k];
                    }
                }
            }
            #endregion
        }

        /// <summary>
        /// 生成乘客个体
        /// </summary>
        /// <param name="time">上一个采样点（上一个乘客到达时间）</param>
        /// <returns></returns>
        private double Creator(double time)
        {
            Person p = new Person();

            #region 计算到达时间(Caculate the arrival time)
            IDistribution distr = new PoissonDistr(rnd, Lambda);
            p.arriveTime = time + distr.Sample();
            #endregion

            #region 计算起始楼层(Caculate the initial floor)
            double rndValue = rnd.NextDouble();
            for (int i = 0; i < n; i++)
            {
                if (rndValue < originQ[i])
                {
                    p.originFloor = i;
                    break;
                }
            }
            #endregion

            #region 计算目标楼层(Caculate the target floor)
            rndValue = rnd.NextDouble();
            for (int j = 0; j < n; j++)
            {
                if (rndValue < ODQ[p.originFloor, j])
                {
                    p.targetFloor = j;
                    break;
                }
            }
            #endregion

            #region 输出(Output)
            this.Output(p);
            #endregion

            return p.arriveTime;
        }
        #endregion

    }
}
